﻿#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

// 窗口尺寸
#define WIDTH 800
#define HEIGHT 600

// Pong游戏参数
#define PADDLE_WIDTH 10
#define PADDLE_HEIGHT 80
#define BALL_RADIUS 8
#define COURT_DEPTH 10

// 游戏状态
float paddle1Y = 0.0;
float paddle2Y = 0.0;
float ballX = 0.0, ballY = 0.0, ballZ = -COURT_DEPTH / 2 - 2;
float ballVelX = 5.0, ballVelY = 3.0, ballVelZ = 0.0;
int score1 = 0, score2 = 0;

// 函数声明
void resetBall(void);

void init(void) {
    // 设置清屏颜色
    glClearColor(0.1, 0.1, 0.1, 0.0);

    // 启用深度测试和光照
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);

    // 设置光源1（主光源 - 白色）
    GLfloat light0_position[] = { 5.0, 5.0, 5.0, 1.0 };
    GLfloat light0_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    GLfloat light0_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light0_specular[] = { 1.0, 1.0, 1.0, 1.0 };

    glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
    glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
    glEnable(GL_LIGHT0);

    // 设置光源2（环境光 - 蓝色）
    GLfloat light1_ambient[] = { 0.0, 0.0, 0.5, 1.0 };
    glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient);
    glEnable(GL_LIGHT1);

    // 设置材质属性（红色球拍）
    GLfloat mat_ambient[] = { 0.5, 0.0, 0.0, 1.0 };
    GLfloat mat_diffuse[] = { 1.0, 0.0, 0.0, 1.0 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 50.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
}

// 绘制球拍
void drawPaddle(float x, float y) {
    glPushMatrix();
    glTranslatef(x, y, 0.0);
    glutSolidCube(PADDLE_WIDTH);
    glPopMatrix();
}

// 绘制球
void drawBall() {
    GLfloat mat_ambient[] = { 0.0, 0.5, 0.5, 1.0 };
    GLfloat mat_diffuse[] = { 0.0, 1.0, 1.0, 1.0 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 50.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

    glPushMatrix();
    glTranslatef(ballX, ballY, ballZ);
    glutSolidSphere(BALL_RADIUS, 20, 20);
    glPopMatrix();
}

// 绘制游戏场地
void drawCourt() {
    // 地面
    GLfloat mat_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
    GLfloat mat_diffuse[] = { 0.8, 0.8, 0.8, 1.0 };
    GLfloat mat_specular[] = { 0.5, 0.5, 0.5, 1.0 };
    GLfloat mat_shininess[] = { 30.0 };

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

    glPushMatrix();
    glTranslatef(0.0, 0.0, -COURT_DEPTH / 2);
    glScalef(100.0, 1.0, COURT_DEPTH);
    glutSolidCube(1.0);
    glPopMatrix();

    // 边界
    glPushMatrix();
    glTranslatef(0.0, 50.0, -COURT_DEPTH / 2);
    glScalef(100.0, 1.0, COURT_DEPTH);
    glutSolidCube(1.0);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(50.0, 0.0, -COURT_DEPTH / 2);
    glScalef(1.0, 100.0, COURT_DEPTH);
    glutSolidCube(1.0);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(-50.0, 0.0, -COURT_DEPTH / 2);
    glScalef(1.0, 100.0, COURT_DEPTH);
    glutSolidCube(1.0);
    glPopMatrix();
}

// 更新游戏状态
void update(int value) {
    // 更新球的位置
    ballX += ballVelX * 0.1;
    ballY += ballVelY * 0.1;
    ballZ += ballVelZ * 0.1;

    // 检测边界碰撞
    if (ballY > 50 - BALL_RADIUS || ballY < -50 + BALL_RADIUS) {
        ballVelY = -ballVelY;
    }

    if (ballX > 50 - BALL_RADIUS) {
        // 右边界，玩家1得分
        score1++;
        resetBall();
    }

    if (ballX < -50 + BALL_RADIUS) {
        // 左边界，玩家2得分
        score2++;
        resetBall();
    }

    // 检测与球拍的碰撞
    if (ballX > 45 - BALL_RADIUS && ballX < 45 + PADDLE_WIDTH / 2 &&
        ballY > paddle1Y - PADDLE_HEIGHT / 2 && ballY < paddle1Y + PADDLE_HEIGHT / 2) {
        ballVelX = -ballVelX * 1.1; // 增加速度
        ballVelY = (ballY - paddle1Y) * 0.1; // 根据碰撞位置改变角度
    }

    if (ballX < -45 + BALL_RADIUS && ballX > -45 - PADDLE_WIDTH / 2 &&
        ballY > paddle2Y - PADDLE_HEIGHT / 2 && ballY < paddle2Y + PADDLE_HEIGHT / 2) {
        ballVelX = -ballVelX * 1.1; // 增加速度
        ballVelY = (ballY - paddle2Y) * 0.1; // 根据碰撞位置改变角度
    }

    // 重绘
    glutPostRedisplay();
    glutTimerFunc(30, update, 0);
}

void resetBall() {
    ballX = 0.0;
    ballY = 0.0;
    ballZ = -COURT_DEPTH / 2 - 2;

    // 随机初始速度
    ballVelX = (rand() % 200 - 100) * 0.05;
    ballVelY = (rand() % 200 - 100) * 0.05;

    // 确保速度不为零
    if (ballVelX == 0) ballVelX = 5.0;
    if (ballVelY == 0) ballVelY = 3.0;
}


void keyboard(unsigned char key, int x, int y) {
    switch (key) {
    case 'w': case 'W':
        paddle1Y += 10.0;
        break;
    case 's': case 'S':
        paddle1Y -= 10.0;
        break;
    case 'i': case 'I':
        paddle2Y += 10.0;
        break;
    case 'k': case 'K':
        paddle2Y -= 10.0;
        break;
    case 27: // ESC键退出
        exit(0);
        break;
    }

    // 限制球拍移动范围
    paddle1Y = fminf(fmaxf(paddle1Y, -50 + PADDLE_HEIGHT / 2), 50 - PADDLE_HEIGHT / 2);
    paddle2Y = fminf(fmaxf(paddle2Y, -50 + PADDLE_HEIGHT / 2), 50 - PADDLE_HEIGHT / 2);
}
void display(void) {
    // 清除颜色和深度缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 设置视图矩阵
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

    // 绘制游戏场地
    drawCourt();

    // 绘制球拍（玩家1 - 红色）
    drawPaddle(45, paddle1Y);

    // 绘制球拍（玩家2 - 蓝色）
    GLfloat mat_ambient[] = { 0.0, 0.0, 0.5, 1.0 };
    GLfloat mat_diffuse[] = { 0.0, 0.0, 1.0, 1.0 };
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    drawPaddle(-45, paddle2Y);

    // 绘制球
    drawBall();

    // 绘制分数
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(-50, 50, -50, 50);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    char scoreText[50];
    sprintf_s(scoreText, sizeof(scoreText), "%d - %d", score1, score2);
    glRasterPos2f(0, 40);

    for (int i = 0; i < (int)strlen(scoreText); i++) {
        glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, scoreText[i]);
    }

    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);

    // 交换缓冲区
    glutSwapBuffers();
}

// 窗口大小改变回调
void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 200.0);
    glMatrixMode(GL_MODELVIEW);
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(WIDTH, HEIGHT);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Pong游戏 - 光照明算法实验");
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutTimerFunc(30, update, 0);
    glutMainLoop();
    return 0;
}